package org.nerve.webmagic;

import org.apache.commons.lang3.StringUtils;
import org.openqa.selenium.*;
import org.openqa.selenium.phantomjs.PhantomJSDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriverService;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import us.codecraft.webmagic.Site;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * org.nerve.webmagic.spider
 *
 * 参考us.codecraft.webmagic.downloader.selenium.WebDriverPool
 *
 * Created by zengxm on 2016/4/20 0020.
 */
public final class PhantomJsDriverPool {
	private static Logger logger= LoggerFactory.getLogger(PhantomJsDriverPool.class);

	private final static int DEFAULT_CAPACITY = 5;

	private final int capacity;

	private final static int STAT_RUNNING = 1;

	private final static int STAT_CLODED = 2;

	private AtomicInteger stat = new AtomicInteger(STAT_RUNNING);

	/*
	 * new fields for configuring phantomJS
	 */
	private WebDriver driver = null;
	private boolean mAutoQuitDriver = true;

	/**
	 * store webDrivers created
	 */
	private List<WebDriver> webDriverList = Collections.synchronizedList(new ArrayList<WebDriver>());
	private BlockingDeque<WebDriver> innerQueue = new LinkedBlockingDeque<>();

	public PhantomJsDriverPool(int capacity) {
		this.capacity = capacity;
	}

	public PhantomJsDriverPool() {
		this(DEFAULT_CAPACITY);
	}

	protected void checkRunning() {
		if (!stat.compareAndSet(STAT_RUNNING, STAT_RUNNING)) {
			throw new IllegalStateException("Already closed!");
		}
	}

	/**
	 *
	 * @return
	 * @throws InterruptedException
	 */
	public WebDriver get(final Site site) throws InterruptedException {
		checkRunning();
		logger.info("尝试获取WebDriver::::::::"+Thread.currentThread().getName());
		WebDriver poll = innerQueue.poll();
		logger.info("尝试获取WebDriver::::::::"+Thread.currentThread().getName()+", poll="+poll);
		if (poll != null) {
			logger.info("----------------获取WebDriver，不为空，当前size="+innerQueue.size()+", capacity="+capacity);
			return poll;
		}
		if (webDriverList.size() < capacity) {
			synchronized (webDriverList) {
				if (webDriverList.size() < capacity) {
					// add new WebDriver instance into pool
					try {
						configure(site);
						innerQueue.add(driver);
						webDriverList.add(driver);
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}else{
			logger.info("-----------------poll is null，想要创建新的driver，但是webDriverList.size()={}, max={}", webDriverList.size(), capacity);
		}
		return innerQueue.take();
	}

	public void returnToPool(WebDriver webDriver) {
		checkRunning();
		logger.info("归还WebDriver::::::::"+Thread.currentThread().getName());
		innerQueue.add(webDriver);
	}


	/**
	 * 创建WebDriver
	 * 读取Site中配置信息（包括Proxy，userUgent）
	 * @param site
	 * @throws IOException
	 */
	public void configure(final Site site) throws IOException {
		DesiredCapabilities caps = DesiredCapabilities.phantomjs();

		/*
		注意：判断平台是Windows还是Linux（只支持64位系统）
		 */
		//判断PhantomJS文件是否存在
		Platform platform=Platform.getCurrent().family();
		File file=new File(this.getClass().getResource("/").getPath()
				+"phantomjs"
				+(platform==Platform.WINDOWS?".exe":""));
		System.out.println("current Platform:"+platform+","+(platform==Platform.WINDOWS));
		if(!file.exists()){
			logger.error(file.getAbsolutePath()+" must exist!");

			throw new FileNotFoundException("PhantomJS file not found on "+file.getAbsolutePath());
		}

		caps.setCapability("takesScreenshot", true);
		caps.setCapability(
				PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY,
					file.getAbsolutePath()
				 );

		configWithSite(caps, site);

		/*
		 *  增加PhantomJS的启动参数
		 */
		ArrayList<String> cliArgsCap = new ArrayList<>();
		cliArgsCap.add("--web-security=false");
		cliArgsCap.add("--ssl-protocol=any");
		cliArgsCap.add("--ignore-ssl-errors=true");
		caps.setCapability(PhantomJSDriverService.PHANTOMJS_CLI_ARGS,cliArgsCap);

		driver=new PhantomJSDriver(caps);
		WebDriver.Options manage = driver.manage();

		//设置大小为1600
		manage.window().setSize(new Dimension(1600, 800));

		//如果设置了初始化的cookies，则传递给webdriver
		if (site!=null && site.getCookies() != null) {
			for (Map.Entry<String, String> cookieEntry : site.getCookies()
					.entrySet()) {
				Cookie cookie = new Cookie(cookieEntry.getKey(),
						cookieEntry.getValue());
				manage.addCookie(cookie);
			}
		}

		logger.info(">>>>>>>>>>>>> init WebDriver done!"+driver.toString()+" <<<<<<<<<<<<<<<<");
	}

	/**
	 * 配置代理以及userAgent
	 * @param caps
	 * @param site
	 */
	private void configWithSite(final DesiredCapabilities caps,final Site site){
		if(site!=null){
			String proxyString=null;
			if(site.getHttpProxyPool()!=null && site.getHttpProxyPool().isEnable()){
				proxyString=site.getHttpProxyFromPool().toHostString();
			}else if(site.getHttpProxy()!=null){
				proxyString=site.getHttpProxy().toHostString();
			}

			if(proxyString!=null){
				Proxy proxy=new Proxy();
				proxy.setHttpProxy(proxyString).setFtpProxy(proxyString).setSslProxy(proxyString);
				caps.setCapability(CapabilityType.PROXY, proxy);

				logger.info("proxy settings:"+proxyString);
			}

			if(StringUtils.isNotBlank(site.getUserAgent())){
				caps.setCapability(
						PhantomJSDriverService.PHANTOMJS_PAGE_SETTINGS_PREFIX+"userAgent",
						site.getUserAgent());

				logger.info("userAgent settings:"+site.getUserAgent());
			}
		}
	}

	public void closeAll() {
		boolean b = stat.compareAndSet(STAT_RUNNING, STAT_CLODED);
		if (!b) {
			throw new IllegalStateException("Already closed!");
		}
		for (WebDriver webDriver : webDriverList) {
			logger.info("Quit webDriver" + webDriver);
			webDriver.quit();
			webDriver = null;
		}
	}
}
